// -*-c++-*- Copyright (C) 2011 osgPango Development Team
// $Id$

#ifndef OSGPANGO_TEXT
#define OSGPANGO_TEXT

#include <osgPango/Context>
#include <osgPango/String>

namespace osgPango {

//! This class serves as a lightweight wrapper/container around the various options
//! we can pass to the Pango backend layout manager; things like what effect to use,
//! how much width should be available, etc. Remember, however, that this class only
//! modifies the low-level, Pango-specific aspects of text. Things like changing the
//! font size, face, and color are done dynamically using a special HTML-like
//! markup language embedded within the string itself! :)
class OSGPANGO_EXPORT TextOptions: public osg::Referenced {
public:
	//! These are the various alignment enums for the actual TEXT itself;
	//! they do NOT affect the POSITION of the text physically in the
	//! scene as you may think! That is the job of the derived class,
	//! such as TextTransform.
	enum TextAlignment {
		//! Align all text in the normal, left-to-right style.
		TEXT_ALIGN_LEFT,
		
		//! Align all text by putting each row in the center of
		//! it's permissable width.
		TEXT_ALIGN_CENTER,
		
		//! Align all text by forcing each row to push as far as
		//! is permissable with it's allocated width.
		TEXT_ALIGN_RIGHT,
		
		//! Align each row of text such that the first and last words
		//! of every row fall on the left and right sides of the
		//! allocated width; this is achieved by Pango by adding
		//! additional extraneous space to each true space where required.
		TEXT_ALIGN_JUSTIFY
	};
	
	TextOptions(
		TextAlignment a = TEXT_ALIGN_LEFT,
		int           w = -1,
		int           h = -1,
		int           i = -1,
		int           s = -1
	):
	alignment (a),
	width     (w),
	height    (h),
	indent    (i),
	spacing   (s) {
	}

	bool setupPangoLayout(PangoLayout*) const;

	TextAlignment alignment;
	int           width;
	int           height;
	int           indent;
	int           spacing;
};

//! The Text object is the basic "arrange a group of quad Geometry in the scene according
//! to how Pango instructs us" class. It will satisfy the bulk of you high-quality 2D text
//! needs, especially if you use TextTransform.
class OSGPANGO_EXPORT Text: public ContextDrawable {
public:
	typedef std::pair<GlyphCache*, ColorPair>                 GlyphGeometryMapKey;
	typedef std::map<GlyphGeometryMapKey, GlyphGeometryIndex> GlyphGeometryMap;

	enum ColorMode {
		//! Allow the fgcolor and bgcolor values in Pango markup to overwrite the
		//! first and second indices of the ColorPalette.
		COLOR_MODE_MARKUP_OVERWRITE,

		//! ONLY use the values in the ColorPalette; always ignore the Pango
		//! markup values.
		COLOR_MODE_PALETTE_ONLY
	};
	
	Text(ColorMode = COLOR_MODE_MARKUP_OVERWRITE);
	Text(const Text& text);

	virtual ~Text();

	//! This is the method inherited from ContextDrawable; it will be called as a result of:
	//! Context::drawLayout being called with "this" instance.
	virtual void drawGlyphs(PangoFont* font, PangoGlyphString* glyphs, int x, int y);

	// You cannot instaniante Text directly; you need to derive from it and
	// override finalize to actually do something with the _ggMap object
	// internally. A TextTransform object is provided by default.
	virtual bool finalize() = 0;

	virtual void clear();

	void setColorPalette(const ColorPalette&);
	
	//! This version of setText assumes you are using PangoMarkup and does not require
	//! that you pass in a font description. It uses the encoding defined by
	//! OSGPANGO_ENCODING in the osgPango/Export header.
	inline void setText(
		const std::string& str,
		const TextOptions& to = TextOptions()
	) {
		_setText(OSGPANGO_ENCODING, str, "", to);
	}

	//! This version of setText does not allow PangoMarkup, and instead requires that
	//! you pass an additional font description string (something like "verdana 100px").
	//! It uses the encoding defined by OSGPANGO_ENCODING.
	inline void setText(
		const std::string& str,
		const std::string& descr,
		const TextOptions& to = TextOptions()
	) {
		_setText(OSGPANGO_ENCODING, str, descr, to);
	}

	//! Here you can specify the encoding as the first argument. We do it this way rather
	//! than adding it on as a final option because having to always specify a default
	//! TextOptions() is unwieldy and doesn't always make for the most readable code.
	//! PangoMarkup usage is turned on.
	inline void setText(
		String::Encoding   encoding,
		const std::string& str,
		const TextOptions& to = TextOptions()
	) {
		_setText(encoding, str, "", to);
	}

	//! This signature matches the protected _setText method.
	inline void setText(
		String::Encoding   encoding,
		const std::string& str,
		const std::string& descr,
		const TextOptions& to = TextOptions()
	) {
		_setText(encoding, str, descr, to);
	}

	void setAlpha(float alpha) {
		_alpha = alpha;
	}
	
	float getAlpha() const {
		return _alpha;
	}

	const osg::Vec2& getSize() {
		return _size;
	}

	const osg::Vec2& getSize() const {
		return _size;
	}

	//! The origin of a Text object is actually it's upper-left corner; this happens
	//! due to the way osgPango interfaces with Pango and how Pango renders text from
	//! top-to-bottom and lef-to-right (remember that OpenGL people are the
	//! "oddities" in that we expect origins to be, and align with, the bottom left of
	//! a canvas or rendering surface). In most cases this value won't be of much use
	//! in an OpenGL environment, but since I cannot predict all of the possible uses
	//! of text in an application, here it remains. 
	osg::Vec2 getOrigin() {
		return _origin;
	}

	const osg::Vec2& getOrigin() const {
		return _origin;
	}

	//! The baseline of a Text object is the bottom-most "line" that text is rendered
	//! on using Pango internally. Similar to getOrigin(), the value returned by
	//! getBaseline() is based on the value derived while interfacing with Pango, and
	//! won't be of much use without a bit of transformation (though this is handled
	//! for you in other Text getter methods).
	unsigned int getBaseline() const {
		return _baseline;
	}

	ColorMode getColorMode() const {
		return _colorMode;
	}
	
	//! This method differs from getOrigin() in that instead of returning the origin
	//! in Pango space, it returns a 2D vector that you can translate the entire Text
	//! object by in order to align it in OpenGL space to the baseline value.
	osg::Vec2 getOriginBaseline() const;

	//! This method, similar to getOriginBaseline(), returns a translation value
	//! allowing you to align the Text with the furthest, bottom-left coordinate.
	//! This is the most natural alignment point for static text, but is not
	//! reliable for text that will change frequently (like a user input widget).
	//! In those cases, a user should use getOriginBaseline(), so that the
	//! glyphs with parts that render "under" the baseline do not modify the
	//! placement of the Text object too drastically.
	osg::Vec2 getOriginTranslated() const;
	
	void setGlyphRenderer(const std::string& renderer) {
		_glyphRenderer = renderer;
	}
	
	const std::string& getGlyphRenderer() const {
		return _glyphRenderer;
	}

protected:
	// TODO: Investigate removing group here...
	bool _finalizeGeometry(osg::Group* group);

	//! This is the "master" setText method which all others directly call. However,
	//! its argument list can be unwieldy, so we provide the easier-to-use wrappers above.
	//! Furthermore, this is the version any subclasses should override if they chose
	//! to modify Text behavior (unless they want to define their own methods).
	virtual void _setText(
		String::Encoding   encoding,
		const std::string& str,
		const std::string& descr,
		const TextOptions& to = TextOptions()
	);

	GlyphGeometryMap _ggMap;
	osg::Vec2        _size;
	osg::Vec2        _origin;
	int              _baseline;
	float            _alpha;
	bool             _init;
	bool             _finalized;
	std::string      _glyphRenderer;
	ColorMode        _colorMode;
	ColorPalette     _palette;
};

}

#endif
